// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <cmath>
#include <vector>

#include "base/logging.h"
#include "base/time/time.h"
#include "media/base/audio_bus.h"
#include "media/cast/test/utility/audio_utility.h"

namespace media {
namespace cast {

    const double Pi = 3.14159265358979323846;

    TestAudioBusFactory::TestAudioBusFactory(int num_channels,
        int sample_rate,
        float sine_wave_frequency,
        float volume)
        : num_channels_(num_channels)
        , sample_rate_(sample_rate)
        , volume_(volume)
        , source_(num_channels, sine_wave_frequency, sample_rate)
    {
        CHECK_LT(0, num_channels);
        CHECK_LT(0, sample_rate);
        CHECK_LE(0.0f, volume_);
        CHECK_LE(volume_, 1.0f);
    }

    TestAudioBusFactory::~TestAudioBusFactory() { }

    std::unique_ptr<AudioBus> TestAudioBusFactory::NextAudioBus(
        const base::TimeDelta& duration)
    {
        const int num_samples = static_cast<int>((sample_rate_ * duration) / base::TimeDelta::FromSeconds(1));
        std::unique_ptr<AudioBus> bus(AudioBus::Create(num_channels_, num_samples));
        source_.OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0, bus.get());
        bus->Scale(volume_);
        return bus;
    }

    int CountZeroCrossings(const float* samples, int length)
    {
        // The sample values must pass beyond |kAmplitudeThreshold| on the opposite
        // side of zero before a crossing will be counted.
        const float kAmplitudeThreshold = 0.03f; // 3% of max amplitude.

        int count = 0;
        int i = 0;
        float last = 0.0f;
        for (; i < length && fabsf(last) < kAmplitudeThreshold; ++i)
            last = samples[i];
        for (; i < length; ++i) {
            if (fabsf(samples[i]) >= kAmplitudeThreshold && (last < 0) != (samples[i] < 0)) {
                ++count;
                last = samples[i];
            }
        }
        return count;
    }

    // EncodeTimestamp stores a 16-bit number as frequencies in a sample.
    // Our internal code tends to work on 10ms chunks of data, and to
    // make sure the decoding always work, I wanted to make sure that the
    // encoded value can be decoded from 5ms of sample data, assuming a
    // sampling rate of 48Khz, this turns out to be 240 samples.
    // Each bit of the timestamp is stored as a frequency, where the
    // frequency is bit_number * 200 Hz. We also add a 'sense' tone to
    // the output, this tone is 17 * 200 = 3400Hz, and when we decode,
    // we can use this tone to make sure that we aren't decoding bogus data.
    // Also, we use this tone to scale our expectations in case something
    // changed changed the volume of the audio.
    //
    // Normally, we will encode 480 samples (10ms) of data, but when we
    // read it will will scan 240 samples at a time until something that
    // can be decoded is found.
    //
    // The intention is to use these routines to encode the frame number
    // that goes with each chunk of audio, so if our frame rate is
    // 30Hz, we would encode 48000/30 = 1600 samples of "1", then
    // 1600 samples of "2", etc. When we decode this, it is possible
    // that we get a chunk of data that is spanning two frame numbers,
    // so we gray-code the numbers. Since adjacent gray-coded number
    // will only differ in one bit, we should never get numbers out
    // of sequence when decoding, at least not by more than one.

    const double kBaseFrequency = 200;
    const int kSamplingFrequency = 48000;
    const size_t kNumBits = 16;
    const size_t kSamplesToAnalyze = kSamplingFrequency / kBaseFrequency;
    const double kSenseFrequency = kBaseFrequency * (kNumBits + 1);
    const double kMinSense = 1.5;

    bool EncodeTimestamp(uint16_t timestamp,
        size_t sample_offset,
        size_t length,
        float* samples)
    {
        if (length < kSamplesToAnalyze) {
            return false;
        }
        // gray-code the number
        timestamp = (timestamp >> 1) ^ timestamp;
        std::vector<double> frequencies;
        for (size_t i = 0; i < kNumBits; i++) {
            if ((timestamp >> i) & 1) {
                frequencies.push_back(kBaseFrequency * (i + 1));
            }
        }
        // Carrier sense frequency
        frequencies.push_back(kSenseFrequency);
        for (size_t i = 0; i < length; i++) {
            double mix_of_components = 0.0;
            for (size_t f = 0; f < frequencies.size(); f++) {
                mix_of_components += sin((i + sample_offset) * Pi * 2.0 * frequencies[f] / kSamplingFrequency);
            }
            mix_of_components /= kNumBits + 1;
            DCHECK_LE(fabs(mix_of_components), 1.0);
            samples[i] = mix_of_components;
        }
        return true;
    }

    namespace {
        // We use a slow DCT here since this code is only used for testing.
        // While an FFT would probably be faster, it wouldn't be a LOT
        // faster since we only analyze 17 out of 120 frequencies.
        // With an FFT we would verify that none of the higher frequencies
        // contain a lot of energy, which would be useful in detecting
        // bogus data.
        double DecodeOneFrequency(const float* samples,
            size_t length,
            double frequency)
        {
            double sin_sum = 0.0;
            double cos_sum = 0.0;
            for (size_t i = 0; i < length; i++) {
                sin_sum += samples[i] * sin(i * Pi * 2 * frequency / kSamplingFrequency);
                cos_sum += samples[i] * cos(i * Pi * 2 * frequency / kSamplingFrequency);
            }
            return sqrt(sin_sum * sin_sum + cos_sum * cos_sum);
        }
    } // namespace

    // When decoding, we first check for sense frequency, then we decode
    // each of the bits. Each frequency must have a strength that is similar to
    // the sense frequency or to zero, or the decoding fails. If it fails, we
    // move head by 60 samples and try again until we run out of samples.
    bool DecodeTimestamp(const float* samples, size_t length, uint16_t* timestamp)
    {
        for (size_t start = 0;
             start + kSamplesToAnalyze <= length;
             start += kSamplesToAnalyze / 4) {
            double sense = DecodeOneFrequency(&samples[start],
                kSamplesToAnalyze,
                kSenseFrequency);
            if (sense < kMinSense)
                continue;
            bool success = true;
            uint16_t gray_coded = 0;
            for (size_t bit = 0; success && bit < kNumBits; bit++) {
                double signal_strength = DecodeOneFrequency(
                    &samples[start],
                    kSamplesToAnalyze,
                    kBaseFrequency * (bit + 1));
                if (signal_strength < sense / 4) {
                    // Zero bit, no action
                } else if (signal_strength > sense * 0.75 && signal_strength < sense * 1.25) {
                    // One bit
                    gray_coded |= 1 << bit;
                } else {
                    success = false;
                }
            }
            if (success) {
                // Convert from gray-coded number to binary.
                uint16_t mask;
                for (mask = gray_coded >> 1; mask != 0; mask = mask >> 1) {
                    gray_coded = gray_coded ^ mask;
                }
                *timestamp = gray_coded;
                return true;
            }
        }
        return false;
    }

} // namespace cast
} // namespace media
